JavaScript 数据类型

六大数据类型

  1. 值类型(原始类型) —— 栈内存存储
    number、string、boolean、null、undefined、Symbol(ES6)
  2. 引用类型(对象类型) —— 栈内存存储指向堆内存存储位置的指针,堆内存存储内容
    object(Function、Array、Date、Regexp…)

隐式类型转换

加减运算符

5 + '5' → String
5 - '5' → Number
* 加减运算符作为单目运算符时的情况:一律转换为数字,减号识别为负号
+[] → 0
+[1] → 1
+[0, 1] → NaN
+{} → NaN
-[] → -0
-[1] → -1
-[0, 1] → NaN
-{} → NaN

点运算符

通过点运算符访问值类型的属性或调用其方法时,先将值类型转换为对应的临时对象类型:(这类对象可称为包装对象)
number → new Number()
boolean → new Boolen()
string → new String()
再访问ga该临时对象的属性或调用其方法,使用完毕后,则销毁该临时对象。

1
2
3
4
5
6
7
var str1 = "test"                           var str2 = new String("test")
str1.length = 10 str2.length = 10
str1.testStrLen = 10 str2.testStrLen = 10
var res1 = str1.length var res2 = str2.length
console.log(res1) // 输出4 console.log(res2) // 输出4
res1 = str1.testStrLen res2 = str2.testStrLen
console.log(res1) // 输出undefined console.log(res2) // 输出10

* 原型链读取时,以自身属性优先。
str1 == str2 → true
str1 === str2 → false

关系运算符

==/!= 存在隐式类型转换,转换为数字进行比较
===/!== 不转换,类型不一致则判断false
* undefined和null与任何有意义的值比较返回的都是false,但是null与undefined之间互相比较返回的是true:
undefined == false → false
null == false → false
undefined == null → true

if() 条件判断

转换为布尔值用于判断

显式类型转换

Number()、String()、Boolean()…

Number("123.1") → 123.1
Number("123.1a") → NaN

parseInt(string, radix)、parseFloat(string)

  • string
    – 只有string中的第一个数字会被解读
    – 开头和结尾的空格会被自动忽略
    – string的第一个非空格字符若不是数字,则返回NaN
  • radix
    – 解析数字的基数,取值[2, 36]
    – 若省略,则以10为基数;若string的数字以”0x”或”0X”开头,则以16为基数
    – 若取值超出[2, 36]的范围,则返回NaN

parseInt("17",8) → 15 —— 1*8+7
parseInt("0x17") → 23 —— 1*16+7
parseFloat(" 17.12 ") → 17.12
parseFloat("0x17") → 0
parseFloat("34 45 66") → 34
parseFloat("40 years") → 40
parseFloat(" He was 40 ") → NaN

取反(!)、双重取反(!!)

转换为布尔型

类型转换运作模式

转换总表

原始值 转换为字符串 转换为数字 转换为布尔型 转换为对象
undefined “undefined” NaN false Throw Error
null “null” 0 false Throw Error
true “true” 1 - New Boolean(true)
false “false” 0 - New Boolean(false)
“” - 0 false New String(“”)
“0” - 0 true New String(“0”)
“1.2” - 1.2 true New String(“1.2”)
“zero” - NaN true New String(“zero”)
0 “0” - false New Number(0)
1 “1” - true New Number(1)
NaN “NaN” - false New Number(NaN)
{} “[object Object]” 比较复杂 true -
[] “” 0 true -
[1] “1” 1 true -
[0, 1] “0,1” NaN true -
function(){} “function (){}” NaN true -
* 注意:new Boolean(false)是一个对象,转换为布尔型时为true

类型转换规律

1. toString()转换为字符串

[1,2,3].toString() → “1,2,3”
(function(x){f(x);}).toString() → “function(x){f(x);}”
new Date(2017,1,14).toString() → “Tue Feb 14 2017 00:00:00 GMT+0800 (中国标准时间)”
/\d{9}/.toString() → “/\d{9}/“
({x:0,y:1}).toString() → “[object object]”

2. valueOf()方法规律

若存在原始值,则转换为它的原始值;
非日期类型的对象多为复合值,无法真正表示成一个原始值,则简单地返回对象本身(如:数组、函数、正则表达式);
日期类型的对象返回从1970年1月1日以来总的毫秒数。
new Number(1).valueOf() → 1 —— [Number]
new Number('1').valueOf() → 1 —— [Number]
new Number('a').valueOf() → NaN —— [Number]
new Boolean(false).valueOf() → false —— [Boolean]
new Boolean('a').valueOf() → true —— [Boolean]
[1,2,3].valueOf() → [1, 2, 3] —— [Array]
(function(x){f(x);}).valueOf() → ƒ(x){f(x);} —— [Function]
new Date(2017,1,14).valueOf() → 1487001600000 —— [Number]
/\d{9}/.valueOf() → /\d{9}/ —— [RegExp]
({x:0,y:1}).valueOf() → {x: 0, y: 1} —— [Object]

3. 转换字符串或数字的步骤

转换为字符串的具体步骤:

Step1 尝试调用toString()方法,如果方法存在且返回一个原始值(值类型),则将这个值转换为字符串,作为最终结果。
Step2 尝试调用valueOf()方法,如果方法存在且返回一个原始值(值类型),则将这个值转换为字符串,作为最终结果。
Step3 如果toString()和valueOf()均调用失败,则抛出一个类型错误。

转换为数字的具体步骤:

Step1 尝试调用valueOf()方法,如果方法存在且返回一个原始值(值类型),则将这个值转换为数字,作为最终结果。
Step2 尝试调用toString()方法,如果方法存在且返回一个原始值(值类型),则将这个值转换为数字,作为最终结果。
Step3 如果toString()和valueOf()均调用失败,则抛出一个类型错误。

  • 空数组转数字的过程:
    []先调用valueOf(),获得[],不是原始值;
    则再调用toString(),获得"",是原始值,转换为数字0输出。
  • [] == false → true —— []与false均转化为数字0进行比较
    ![] == false → true —— []先转换为布尔型true取反,再转换为数字0false转换的数字0进行比较
关系、算术运算符至少有一侧为对象类型时

1) 对于非日期类型的对象,先调用valueOf(),再调用toString(),直到获得原始值,再根据运算需要转换类型;
2) 对于日期类型的对象,如果是+、==、!=运算符,则调用toString()获得原始值,其他运算符(如减号、大于号、小于号),则调用valueOf()获得原始值,再参与运算:
new Date(2017, 1, 1) == 1485878400000 → false
new Date(2017, 1, 1) == 'Wed Feb 01 2017 00:00:00 GMT+0800 (中国标准时间)' → true
new Date(2017, 1, 1) + 1 → “Wed Feb 01 2017 00:00:00 GMT+0800 (中国标准时间)1”
new Date(2017, 1, 1) - 1 → 1485878399999
new Date(2018, 0, 1) < new Date(2017, 2, 1) → false
"Mon Jan 01 2018 00:00:00 GMT+0800 (中国标准时间)" < "Wed Mar 01 2017 00:00:00 GMT+0800 (中国标准时间)" → true

类型判别方法

1. typeof

一元运算符,置于一个运算数之前:typeof 检测对象,检测对象也可以用()包裹;
运算结果是一个字符串,表示运算数的类型:

检测对象 检测结果
undefined “undefined”
null “object”
true “boolean”
123 “number”
NaN “number”
“123” “string”
[] “object”
{} “object”
function(x){f(x);} “function”
Symbol类型变量(ES6新类型) “symbol”
总结:typeof()可以判别原始类型(除null),不能判断对象类型(除function)
应用场景:
- 判断一个变量是否存在
if(typeof a != "undefined")(不使用if(a)可防止a变量未声明而报错)
- 判断document.getElementsByTagName是否获取到对象
if(typeof(list.length) != "undefined" )if(!isNaN(list.length))

2. instanceof

二元运算符,object(要检测的对象) instanceof constructor(某个构造函数),判断检测对象的原型链中是否存在构造函数的prototype属性,即判断某个对象是不是另一个对象的实例。
仅能判别对象类型,不能判别原始类型:

  • 内置对象类型
    [] instanceof Object → true
    [] instanceof Array → true
    [] instanceof Date → false
  • 自定义对象类型及其继承关系
    1
    2
    3
    4
    5
    6
    7
    8
    9
    function Person(){}
    function Student(){}
    var p = new Person()
    Student.prototype = p // Student原型继承p,Student.prototype._proto_ = p.prototype
    var s = new Student()
    console.log(s instanceof Student) // 输出true
    console.log(s instanceof Person) // 输出true
    Student.prototype = {}
    console.log(s instanceof Student) // 输出false

instanceof的模拟函数:

1
2
3
4
5
6
7
8
9
10
11
function _instanceof(A, B) {
var O = B.prototype // 取B的显示原型
A = A._proto_ // 取A的隐式原型
while (true) {
if (A === null) // Object.prototype._proto_ === null
return false
if (O === A) // 当O严格等于A时,返回true
return true
A = A._proto_
}
}

因此:
1) Object instanceof Object → true
第一个Object的原型链:
Object → (Object._proto_) → Function.prototype → (Function.prototype._proto_) → Object.prototype
第二个Object的原型:Object → Object.prototype

2) Function instanceof Function → true
第一个Function的原型链:
Function => (Function._proto_) => Function.prototype
第二个Function的原型:Function => Function.prototype

3) console.log(Function instanceof Object) → true
Function的原型链:
Function => (Function._proto_) => Function.prototype => (Function.prototype._proto_) => Object.prototype
Object的原型:Object => Object.prototypefunction Person() {}

4) console.log(Person instanceof Function) → true
Person的原型链:Person => (Person._proto_) => Function.prototype
Function的原型:Function => Function.prototype

5) console.log(String instanceof String) → false
第一个String的原型链:
String => (String._proto_) => Function.prototype => (Function.prototype._proto_) => Object.prototype
第二个String的原型:String => String.prototype

6) console.log(Boolean instanceof Boolean) → false
同理String

7) console.log(Person instanceof Person) → false
第一个Person的原型链:
Person => (Person._proto_) => Function.prototype => (Function.prototype._proto_) => Object.prototype
第二个Person的原型:Person => Person.prototype

3. Object.prototype.toString.call(检测对象)

检测对象 检测结果
undefined “[object Undefined]”
null “[object Null]”
true “[object Boolean]”
123 “[object Number]”
NaN “[object Number]”
“123” “[object String]”
[] “[object Array]”
{} “[object Object]”
function(x){f(x);} “[object Function]”
* 对于没有重写toString方法的自定义对象类型,返回均为[object Object],其构造函数返回[object Function],如:
1
2
3
4
function Person() {}
var p = new Person()
Object.prototype.toString.call(Person) // 输出"[object Function]"
Object.prototype.toString.call(p) // 输出"[object Object]"

4. (检测对象).constructor

具体操作:
(检测对象).constructor.toString().match(/function\s*([^(]*)/)[1](匹配正则没懂)
(检测对象).constructor.toString().split(/ |\(/)[1]
基本可以判别所有类型:

1
2
3
4
function Person() {}
var p = new Person()
(Person).constructor.toString().split(/ |\(/)[1] // 输出"Function"
(p).constructor.toString().split(/ |\(/)[1] // 输出"Person"